﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.IO;

namespace LDAPaper
{
    public static class Tester
    {
        private static int[,] ConfusionMatrix;
        private static double[,] NormalizedConfusionMatrix;
        private static int[] GestureTypeIndexes;
        
        private static string ConstructNewFileName()
        {
            int id = 0;
            string fileName = string.Empty;

            do
            {
                fileName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\TestResult_" + id.ToString("D2") + ".txt";
                id++;
            }
            while (File.Exists(fileName));

            return fileName;
        }

        public static void Test(GestureDB db, Algorithm algorithm0)
        {
            Tester.ConfusionMatrix = new int[db.TotalClassCount, db.TotalClassCount];
            Tester.GestureTypeIndexes = db.GestureTyepIndexes;

            int processPercent = 0;

            string fileName = ConstructNewFileName();

            string[,] ConfusionMatrix = new string[db.TotalClassCount + 1, db.TotalClassCount + 1];

            using (TextWriter writer = File.CreateText(fileName))
            {
                writer.WriteLine("Parameters:");
                writer.WriteLine();

                writer.WriteLine("Silence length     : " + Parameters.SilenceLength);
                writer.WriteLine("Silence sigma      : " + Parameters.SilenceSigma);
                writer.WriteLine("Additive sigma     : " + Parameters.AdditiveSigma);
                writer.WriteLine("Random try count   : " + Parameters.RandomTryCount);
                writer.WriteLine("DTW Type           : " + Parameters.DTWType.ToString());
                writer.WriteLine("Coordinate Type    : " + Parameters.CoordinateType.ToString());
                writer.WriteLine("Used feature count : " + Parameters.UsedFeatureCount.ToString());

                writer.Write("Starting time: ");
                writer.WriteLine(DateTime.Now);

                algorithm0.Train(db);

                int totalTrueCount = 0;
                int totalFalseCount = 0;

                double totalDiscriminantRatio = 0;

                foreach (List<Gesture> gestureClass in db)
                {
                    int classTrueCount = 0;
                    int classFalseCount = 0;

                    double classDiscriminantRatio = 0;

                    writer.WriteLine("\nCurrent class:" + gestureClass[0].gestureType);

                    foreach (Gesture gestureSample in gestureClass)
                    {
                        for (int i = 0; i < Parameters.RandomTryCount; i++)
                        {
                            gestureSample.RefreshRandomness();

                            RecognizeResult result0 = algorithm0.Recognize(gestureSample);

                            UpdateConfusionMatrix(gestureSample.gestureType, result0.GestureType);

                            if (result0.GestureType == gestureSample.gestureType)
                                classTrueCount++;
                            else
                                classFalseCount++;

                            classDiscriminantRatio += result0.Ratio;
                        }
                    }

                    classDiscriminantRatio = classDiscriminantRatio / (gestureClass.Count * Parameters.RandomTryCount);

                    totalDiscriminantRatio += classDiscriminantRatio;

                    totalTrueCount += classTrueCount;
                    totalFalseCount += classFalseCount;

                    double classAccuracy = (double)classTrueCount / (gestureClass.Count * Parameters.RandomTryCount);

                    writer.WriteLine("Class Accuracy: %" + (classAccuracy * 100).ToString(CultureInfo.InvariantCulture));
                    writer.WriteLine("Class Discriminant Ratio: " + classDiscriminantRatio);

                    writer.WriteLine();

                    processPercent++;
                    Console.WriteLine(processPercent.ToString() + "/" + db.TotalClassCount.ToString() + " finished.");
                }

                double totalAccuracy = (double)totalTrueCount / (db.TotalSampleCount * Parameters.RandomTryCount);
                totalDiscriminantRatio = totalDiscriminantRatio / db.TotalClassCount;

                writer.WriteLine("Total Accuracy: %" + (totalAccuracy * 100).ToString(CultureInfo.InvariantCulture));
                writer.WriteLine("Total Discriminant Ratio: " + totalDiscriminantRatio);
            }

            WriteConfusionMatrix();
        }

        private static void UpdateConfusionMatrix(GestureType knownClass, GestureType calculated)
        {
            int row = Array.IndexOf(Tester.GestureTypeIndexes, (int)knownClass);
            int col = Array.IndexOf(Tester.GestureTypeIndexes, (int)calculated);

            ConfusionMatrix[row, col] += 1;
        }

        private static void NormalizeConfusionMatrix()
        {
            NormalizedConfusionMatrix = new double[ConfusionMatrix.GetLength(0), ConfusionMatrix.GetLength(1)];

            for (int i = 0; i < ConfusionMatrix.GetLength(0); i++)
            {
                double sum = 0;

                for (int j = 0; j < ConfusionMatrix.GetLength(1); j++)
                {
                    sum += ConfusionMatrix[i, j];
                }

                for (int j = 0; j < ConfusionMatrix.GetLength(1); j++)
                {
                    NormalizedConfusionMatrix[i, j] = (ConfusionMatrix[i, j] / sum) * 100;
                }
            }
        }

        private static void WriteConfusionMatrix()
        {
            NormalizeConfusionMatrix();

            using (TextWriter writer = File.CreateText(@"C:\Users\saitcelebi\Desktop\conf_matrix.txt"))
            {
                writer.Write("{0,-20}", " ");

                for (int i = 0; i < Tester.GestureTypeIndexes.Length; i++)
   			    {
                    writer.Write("{0,-20}", ((GestureType)Tester.GestureTypeIndexes[i]).ToString());
			    }

                writer.WriteLine();

                for (int i = 0; i < ConfusionMatrix.GetLength(0); i++)
                {
                    writer.Write("{0,-20}", ((GestureType)Tester.GestureTypeIndexes[i]).ToString());

                    for (int j = 0; j < ConfusionMatrix.GetLength(1); j++)
                    {
                        writer.Write("{0,-20}", Tester.NormalizedConfusionMatrix[i, j].ToString());
                    }

                    writer.WriteLine();
                }
            }
        }
    }
}
